﻿namespace ServerApp.DAL
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Server;

    /// <summary>
    /// Class for working with orders
    /// </summary>
    public class OrderService
    {

        #region publicMethods

        /// <summary>
        /// Gets the order by its id
        /// </summary>
        /// <param name="orderId">Id of the order</param>
        /// <param name="language">Language of the text</param>
        /// <returns>OrderFull value of the order</returns>
        public static OrderFull GetOrder(long orderId, string language)
        {
            try
            {
                InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
                OrderFull order = null;

                if (entity.Orders != null && entity.Orders.Count() > 0)
                {
                    ServerApp.Order temp = entity.Orders.Where(o => o.OrderId == orderId).FirstOrDefault();
                    ///order
                    //order
                    if (temp != null)
                    {
                        order = new OrderFull(temp.OrderId);
                        order.Comment = temp.Comment;
                        order.CreatedDateTime = temp.CreateDateTime;
                        order.PlaceId = temp.PlaceId;
                        order.Status = (OrderStatus)temp.Status;

                        if (temp.CompleteDateTime.HasValue)
                            order.ClosedDateTime = temp.CompleteDateTime.Value;

                        //customer
                        if (temp.FK_CustomerId.HasValue)
                        {
                            Customer customer = entity.Customers.Where(c => c.CustomerId == temp.FK_CustomerId).FirstOrDefault();
                            order.Customer = new User(temp.FK_CustomerId.Value)
                            {
                                Login = customer.Login,
                                Password = customer.Password,
                                Phone = customer.Phone,
                                Role = (UserRole)customer.Role,
                                Email = customer.Email,
                                FIO = customer.FIO,
                                PlaceId = customer.FK_PlaceId,
                            };
                        }

                        //executor
                        if (temp.FK_ExecuterId.HasValue)
                        {
                            Customer tempExecutor = entity.Customers.Where(c => c.CustomerId == temp.FK_ExecuterId).FirstOrDefault();
                            order.Executor = new User(temp.FK_ExecuterId.Value)
                            {
                                Login = tempExecutor.Login,
                                Password = tempExecutor.Password,
                                Phone = tempExecutor.Phone,
                                Role = (UserRole)tempExecutor.Role,
                                Email = tempExecutor.Email,
                                FIO = tempExecutor.FIO,
                                PlaceId = tempExecutor.FK_PlaceId,
                            };
                        }

                        //products 
                        List<OrderProduct> listOrderProduct = entity.OrderProducts.Where(o => o.FK_OrderId == temp.OrderId).ToList();
                        if (listOrderProduct != null)
                        {
                            order.Composition = new List<OrderPosition>();
                            foreach (OrderProduct tempOrderProduct in listOrderProduct)
                            {
                                Product tempProduct = entity.Products.Where(p => p.ProductId == tempOrderProduct.FK_ProductId).FirstOrDefault();

                                ProductView product = new ProductView(tempProduct.ProductId)
                                {
                                    Photo = tempProduct.Photo,
                                    Price = Convert.ToInt32(tempProduct.Price),
                                    CategoryId = tempProduct.FK_ProductCategoryId
                                };

                                if (language == null)
                                    product.Name = entity.ProductLocalizations.Where(l => l.FK_ProductId == tempProduct.ProductId).FirstOrDefault().Name;
                                else
                                {
                                    if (entity.ProductLocalizations.Where(l => l.FK_ProductId == tempProduct.ProductId && l.Language == language).FirstOrDefault() != null)
                                        product.Name = entity.ProductLocalizations.Where(l => l.FK_ProductId == tempProduct.ProductId && l.Language == language).FirstOrDefault().Name;
                                }

                                byte status = Convert.ToByte(ProductItemStatus.Reserved);
                                int reserveNumber = entity.ProductItems.Where(p => p.FK_OrderID == order.Id && p.FK_ProductId == product.Id && p.Status == status).ToList().Count();
                                order.Composition.Add(new OrderPosition(product, order.Id)
                                {                                    
                                    Count = tempOrderProduct.Count,
                                    Cost = tempOrderProduct.Price,
                                    CountUnreserved = tempOrderProduct.Count - reserveNumber
                                });
                            }
                        }
                    }
                }
                ////   return order;

                if (order != null)
                {
                    return order;
                }
                else
                {
                    return OrderFull.Null;
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Reserves the order
        /// </summary>
        /// <param name="orderId">order id</param>
        /// <returns>changed OrderFull value</returns>
        public static OrderFull ReserveOrder(long orderId, long executorId)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            if (entity.Orders != null && entity.Orders.Count() > 0 && entity.Customers != null && entity.Customers.Count() > 0)
            {
                Order order = entity.Orders.Where(o => o.OrderId == orderId).First();
                if (executorId > 0)
                    order.FK_ExecuterId = executorId;
                entity.SaveChanges();
            }
            changeStatus(orderId, OrderStatus.Worked);
            return GetOrder(orderId, null);
        }

        /// <summary>
        /// Reserves products for the order
        /// </summary>
        /// <param name="orderId">Id of the order</param>
        /// <param name="productId">Id of the product</param>
        /// <param name="count">number of the products</param>
        /// <returns>difference between number of the order products and real products</returns>
        public static int ReserveProduct(long orderId, long productId, int count)
        {            
            int counter = count;
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            if (entity.Orders != null && entity.Orders.Count() > 0 && entity.ProductItems != null && entity.ProductItems.Count() > 0)
            {
                byte status = Convert.ToByte(ProductItemStatus.Non);
                int number = entity.ProductItems.Where(o => o.FK_ProductId == productId && o.Status == status).ToList().Count;
                if (number > 0)
                {
                    while (counter > 0)
                    {
                        ProductItem item = entity.ProductItems.Where(o => o.FK_ProductId == productId && o.Status == status).First();
                        if (item != null)
                        {
                            item.FK_OrderID = orderId;
                            item.Status = Convert.ToByte(ProductItemStatus.Reserved);
                            entity.SaveChanges();
                            counter--;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            return counter;
        }

        /// <summary>
        /// Updates the order
        /// </summary>
        /// <param name="newOrder">OrderFull value of the order</param>
        /// <returns>OrderFull value of the updated order</returns>
        public  static OrderFull UpdateOrder(OrderFull newOrder)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            try
            {                              
                if (newOrder.Composition != null)
                {
                    //update all the products of the order                        
                    for (int i = 0; i < newOrder.Composition.Count(); i++)
                    {
                        long temp = newOrder.Composition[i].Product.Id;
                        OrderProduct orderProduct = entity.OrderProducts.Where(o => o.FK_OrderId == newOrder.Id && o.FK_ProductId == temp).FirstOrDefault();
                        orderProduct.Count = newOrder.Composition[i].Count;                                                
                        entity.SaveChanges();
                    }
                }
                return GetOrder(newOrder.Id, null);
            }
            catch
            {
                return null;
            }            
        }

        /// <summary>
        /// Closes the order
        /// </summary>
        /// <param name="orderId">order id</param>
        /// <returns>changed OrderFull value</returns>
        public static OrderFull CloseOrder(long orderId)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            if (entity.Orders != null && entity.Orders.Count() > 0)
            {
                Order order = entity.Orders.Where(o => o.OrderId == orderId).FirstOrDefault();
                order.Status = Convert.ToByte(OrderStatus.Closed);
                order.CompleteDateTime = DateTime.Now;
                entity.SaveChanges();
            }       
   
            return GetOrder(orderId, null);
        }

        /// <summary>
        /// Cancel the order and unreserves all products of this order
        /// </summary>
        /// <param name="orderId">Id of the order</param>
        /// <returns>bool value</returns>
        public static bool CancelOrder(long orderId)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            try
            {
                if (entity.Orders != null && entity.Orders.Count() > 0 && entity.ProductItems != null && entity.ProductItems.Count() > 0)
                {
                    Order order = entity.Orders.Where(o => o.OrderId == orderId).First();
                    order.Status = Convert.ToByte(OrderStatus.Canceled);

                    List<ProductItem> productItems = entity.ProductItems.Where(p => p.FK_OrderID == orderId).ToList();

                    if (productItems != null)
                    {
                        foreach (ProductItem item in productItems)
                        {
                            item.Status = Convert.ToByte(ProductItemStatus.Non);
                        }
                    }

                    entity.SaveChanges();
                }
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Saves order to the db
        /// </summary>
        /// <param name="custumerId">id of the customer</param>      
        /// <param name="products">Dictionary of the products [productId - count]</param>
        /// <param name="placeId">Id place of the order destination</param>
        /// <param name="whoProcessId">Id of the ecxecutor. If 0 - order is free</param>
        /// <returns>new value of the orderView</returns>
        public static OrderFull SaveOrder(long? custumerId, Dictionary<long, int> products, int placeId, long? whoProcessId)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            try
            {
                //fill the fields of the order
                Order order = new Order()
                    {
                        FK_CustomerId = custumerId,
                        PlaceId = placeId,
                        FK_ExecuterId = whoProcessId,
                        CreateDateTime = DateTime.Now,
                        Status = Convert.ToByte(OrderStatus.Created)
                    };

                //add new order to the db           
                entity.Orders.AddObject(order);
                entity.SaveChanges();

                if (products != null)
                {
                    //fill all the products of the order                        
                    foreach (int productId in products.Keys)
                    {
                        OrderProduct orderProduct = new OrderProduct();
                        orderProduct.FK_ProductId = productId;
                        orderProduct.Count = products[productId];
                        orderProduct.FK_OrderId = order.OrderId;
                        //gets the price of the product on the moment of the order
                        orderProduct.Price = entity.Products.Where(p => p.ProductId == productId).FirstOrDefault().Price;
                        //add new orderProduct to db
                        entity.OrderProducts.AddObject(orderProduct);
                        entity.SaveChanges();
                    }
                }
                return GetOrder(order.OrderId, null);
            }
            catch
            {
                return null;
            }

        }

        /// <summary>
        /// Gets the history of the orders of the one customer
        /// </summary>
        /// <param name="custumerId">customer id</param>
        /// <param name="count">count of the orders for review</param>
        /// <returns>list of the OrderView objects</returns>
        public static List<OrderView> GetOrderHistory(long custumerId, int count)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            List<OrderView> orderList = new List<OrderView>();

            List<Order> temp = entity.Orders.Where(o => o.FK_CustomerId == custumerId).ToList();

            int counter = 0;
            foreach (Order orderTemp in temp)
            {
                if (counter < count)
                {
                    //addition order to the list 
                    orderList.Add(fillOrderViewObject(orderTemp, entity));
                }
                else
                {
                    break;
                }
                counter++;
            }
            return orderList;
        }

        /// <summary>
        /// Gets the list of the orders views
        /// </summary>        
        /// <param name="page">Number of the page</param>        
        /// <param name="filter">Dictionary<OrderFilter.value,object></param>
        /// <param name="numberOnThePage">Number of the orders on the one page</param>
        /// <returns>list of the orderView</returns>   
        public static List<OrderView> GetOrderList(Dictionary<OrderFilter, object> filter, int page, int numberOnThePage)
        {
            try
            {
                InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
                List<OrderView> orderList = null;

                if (entity.Orders != null && entity.Orders.Count() > 0)
                {
                    List<Order> temp = entity.Orders.ToList();

                    if (filter != null && temp != null)
                    {
                        foreach (OrderFilter key in filter.Keys)
                        {
                            switch (key)
                            {
                                case OrderFilter.Executor:
                                    {
                                        int e_id = Convert.ToInt32(filter[key]);
                                        temp = temp.Where(o => o.FK_ExecuterId == e_id).ToList();
                                        break;
                                    }
                                case OrderFilter.Customer:
                                    {
                                        int c_id = Convert.ToInt32(filter[key]);
                                        temp = temp.Where(o => o.FK_CustomerId == c_id).ToList();
                                        break;
                                    }
                                case OrderFilter.Place:
                                    {
                                        int c_id = Convert.ToInt32(filter[key]);
                                        temp = temp.Where(o => o.PlaceId == c_id).ToList();
                                        break;
                                    }
                                case OrderFilter.Date:
                                    {
                                        DateTime date = Convert.ToDateTime(filter[key]);
                                        temp = temp.Where(o => o.CreateDateTime == date).ToList();
                                        break;
                                    }
                                case OrderFilter.PriceMax:
                                    {
                                        int price = Convert.ToInt32(filter[key]);
                                        List<Order> secondary = new List<Order>();

                                        foreach (Order ord in temp)
                                        {
                                            if (getOrderPrice(ord.OrderId, entity) < price)
                                            {
                                                secondary.Add(ord);
                                            }
                                        }

                                        temp = secondary;
                                        break;
                                    }
                                case OrderFilter.PriceMin:
                                    {
                                        int price = Convert.ToInt32(filter[key]);
                                        List<Order> secondary = new List<Order>();

                                        foreach (Order ord in temp)
                                        {
                                            if (getOrderPrice(ord.OrderId, entity) >= price)
                                            {
                                                secondary.Add(ord);
                                            }
                                        }

                                        temp = secondary;
                                        break;
                                    }
                            }
                        }
                    }

                    //start number of the orders
                    int orderCounter = page * numberOnThePage - numberOnThePage;

                    //creating list of the OrderView
                    if (orderCounter <= temp.Count())
                    {
                        orderList = new List<OrderView>();

                        while (orderCounter <= page * numberOnThePage)
                        {
                            if (orderCounter < temp.Count())
                            {
                                //addition order to the list 
                                orderList.Add(fillOrderViewObject(temp[orderCounter], entity));
                                orderCounter++;
                            }
                            else
                            {
                                break;
                            }
                        }//while
                    }
                }

                return orderList;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Add a new product to the order
        /// </summary>
        /// <param name="orderId">Id of the order</param>
        /// <param name="productId">Id of the product</param>
        /// <param name="count">Number of the products</param>
        /// <returns>new OrderFUll value of the current order</returns>
        public static OrderFull AddProductToOrder(long orderId, long productId, int count)
        {
            try
            {
                if (orderId > 0 && productId > 0)
                {
                    InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
                    OrderProduct orderProduct = new OrderProduct();
                    orderProduct.FK_ProductId = productId;
                    orderProduct.Count = count;                    
                    orderProduct.FK_OrderId = orderId;
                    //gets the price of the product on the moment of the order
                    orderProduct.Price = entity.Products.Where(p => p.ProductId == productId).FirstOrDefault().Price;
                    //add new orderProduct to db
                    entity.OrderProducts.AddObject(orderProduct);
                    entity.SaveChanges();
                    return GetOrder(orderId, null);
                }
                else
                    return OrderFull.Null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Delete a product from the current order
        /// </summary>
        /// <param name="orderId">Id of the order</param>
        /// <param name="productId">Id of the product</param>
        /// <param name="count">Number of the products</param>
        /// <returns>new OrderFUll value of the current order</returns>
        public static OrderFull DeleteProductFromOrder(long orderId, long productId, int count)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            try
            {
                OrderProduct orderProduct = entity.OrderProducts.Where(p => p.FK_ProductId == productId && p.FK_OrderId == orderId).FirstOrDefault();
                entity.OrderProducts.DeleteObject(orderProduct);
                entity.SaveChanges();

                List<ProductItem> productItems = entity.ProductItems.Where(p => p.FK_OrderID == orderId && p.FK_ProductId == productId).ToList();

                if (productItems != null)
                {
                    foreach (ProductItem item in productItems)
                    {
                        item.Status = Convert.ToByte(ProductItemStatus.Non);
                    }
                }

                entity.SaveChanges();
                return GetOrder(orderId, null);
            }
            catch
            {
                return GetOrder(orderId, null);
            }
        }

        #endregion

        #region privateMethods

        /// <summary>
        /// Changes the status of the order
        /// </summary>
        /// <param name="orderId">order id</param>
        /// <param name="status">OrderStatus value</param>
        private static void changeStatus(long orderId, OrderStatus status)
        {
            InternetShopBaseEntities entity = ServerApp.SingleEntity.GetInstance();
            if (entity.Orders != null && entity.Orders.Count() > 0)
            {
                Order order = entity.Orders.Where(o => o.OrderId == orderId).FirstOrDefault();
                order.Status = Convert.ToByte(status);
                entity.SaveChanges();
            }
        }

        /// <summary>
        /// Gets the price of the order
        /// </summary>
        /// <param name="orderId">The order id</param>
        /// <param name="entity"></param>
        /// <returns>Long value of the order's price</returns>
        private static long getOrderPrice(long orderId, InternetShopBaseEntities entity)
        {
            long price = 0;
            if (entity.OrderProducts != null && entity.OrderProducts.Count() > 0)
            {
                List<OrderProduct> orderProducts = entity.OrderProducts.Where(o => o.FK_OrderId == orderId).ToList();

                if (orderProducts != null)
                {
                    foreach (OrderProduct op in orderProducts)
                    {
                        price += op.Count * Convert.ToInt32(op.Price);
                    }
                }

            }
            return price;
        }

        /// <summary>
        /// Fills the objext of the OrderView
        /// </summary>
        /// <param name="orderTemp">Order object</param>
        /// <param name="entity"></param>
        /// <returns>OrderView object</returns>
        private static OrderView fillOrderViewObject(Order orderTemp, InternetShopBaseEntities entity)
        {
            OrderView order = new OrderView(orderTemp.OrderId)
            {
                Status = (OrderStatus)orderTemp.Status,
                CreatedDateTime = orderTemp.CreateDateTime
            };
            //calculation of the price of the order                 
            order.Price = getOrderPrice(orderTemp.OrderId, entity);
            //customer
            if (orderTemp.FK_CustomerId.HasValue)
            {
                Customer customer = entity.Customers.Where(c => c.CustomerId == orderTemp.FK_CustomerId).FirstOrDefault();
                order.Customer = new User(orderTemp.FK_CustomerId.Value)
                {
                    Login = customer.Login,
                    Password = customer.Password,
                    Phone = customer.Phone,
                    Role = (UserRole)customer.Role,
                    Email = customer.Email,
                    FIO = customer.FIO,
                    PlaceId = customer.FK_PlaceId,
                };
            }

            //executor
            if (orderTemp.FK_ExecuterId.HasValue)
            {
                Customer tempExecutor = entity.Customers.Where(c => c.CustomerId == orderTemp.FK_ExecuterId).FirstOrDefault();
                order.Executor = new User(orderTemp.FK_ExecuterId.Value)
                {
                    Login = tempExecutor.Login,
                    Password = tempExecutor.Password,
                    Phone = tempExecutor.Phone,
                    Role = (UserRole)tempExecutor.Role,
                    Email = tempExecutor.Email,
                    FIO = tempExecutor.FIO,
                    PlaceId = tempExecutor.FK_PlaceId,
                };
            }

            return order;
        }

        #endregion
    }
}
